{
    炎龙出击 v1.0
    炎龙工作室 2017年12月
}
program yanlonggame;
{$apptype gui}
{$MODE objfpc}
{$codepage UTF8}
uses
    windows;
const
    AppName='yanlong';
    AppTitle='炎龙出击 v1.0';
    WINDOWN_WIDTH=520;
    WINDOWN_HEIGHT=800;
    path='./images';        //图片目录
    skip=15;                 //按键移动的
    
    planeWidth=48;
    planeHeight=58;
    enenyWidth=48;
    enenyHeight=58;
    bulletWidth=27;
    bulletHeight=34;

    BulletStyle =1;
    EnenyStyle =2;

type
    node=record
            x:longint;
            y:longint;
            z:longint;
            next:^node;
        end;
    
    pNode=^Node;
    pEneny=^node;
    pPlane=^node;
    pBullet=^node;
    pBottom=^node;
    
var
    g_hdc, g_bufDC, g_MemDC:hdc; //窗口dc, 缓冲， 内存dc;
    bgBMP, enenyBMP, planeBMP, bulletBMP :HBITMAP;  //背景图，敌机图， 自己的战机， 子弹
    g_preTime:DWORD;
    g_shiftX:longint=600;
    showLogo:boolean=true;
    eneny:pEneny=nil;
    plane:pPlane=nil;
    bullet:pBullet=nil;
    bottom:pBottom=nil;
    MaxRight:longint=0;
    MaxBottom:longint=0;
    gaming:boolean=false;

{初始化}
procedure gameinit(window:HWND); forward;
{绘游戏}
procedure gamepaint(window:HWND); forward;
{释放数据}
procedure gamefree(window:HWND); forward;
{增加游戏元素, 1子弹， 2敌人}
procedure addNode(style:integer);forward;
{删除后面元素}
procedure delAfterNode(l:pnode);forward;
{两个距形碰撞}
function checkMeet(x1,y1,w1,h1,x2,y2,w2,h2:longint):boolean;forward;

{初始化}
procedure gameinit(window:HWND);
var
    bmp: HBITMAP;
    r : rect;
begin
    GetClientRect(window,&r);
    MaxRight:=r.right;
    MaxBottom:=r.bottom;    
    g_hdc:=getdc(window);
    g_MemDC := CreateCompatibleDC(g_hdc);
    g_bufDC := CreateCompatibleDC(g_hdc);
    bmp := CreateCompatibleBitmap(g_hdc, WINDOWN_WIDTH, WINDOWN_HEIGHT);

    bgBMP := LoadImage(0,pchar(path+'/bg2.bmp'),IMAGE_BITMAP,600,1500,LR_LOADFROMFILE);
    planeBMP := LoadImage(0,pchar(path+'/I.bmp'),IMAGE_BITMAP,planeWidth*2,planeHeight,LR_LOADFROMFILE);
    enenyBMP := LoadImage(0,pchar(path+'/eneny.bmp'),IMAGE_BITMAP,enenyWidth*2,enenyHeight,LR_LOADFROMFILE);
    bulletBMP := LoadImage(0,pchar(path+'/bullet.bmp'),IMAGE_BITMAP,bulletWidth*2,bulletheight,LR_LOADFROMFILE);
    
    SelectObject(g_MemDC,bmp);
    SelectObject(g_bufDC,bmp);
    DeleteObject(bmp);
    g_preTime:=getTickCount();

    new(plane);
    plane^.x:=700;
    plane^.y:=MaxRight div 2;
    plane^.next:=nil;

    new(eneny);
    eneny^.next:=nil;
    
    new(bullet);
    bullet^.next:=nil;
    gaming:=true;    
end;

{绘游戏}
procedure gamepaint(window:HWND);
var
    p, temp, deltemp:pnode;
begin
    //处理背景 开始
    SelectObject(g_bufDC,bgBMP);
    BitBlt(g_MemDC,0,0,WINDOWN_WIDTH,WINDOWN_HEIGHT,g_bufDC,0,g_shiftX,SRCCOPY);
    if showLogo and (g_shiftX<0) then
    begin
        BitBlt(g_MemDC,0,0,WINDOWN_WIDTH,WINDOWN_HEIGHT,g_bufDC,0,g_shiftX,SRCCOPY);
        BitBlt(g_MemDC,0,0,WINDOWN_WIDTH,WINDOWN_HEIGHT-g_shiftX,g_bufDC,0,g_shiftX+1500,SRCCOPY);
        if(g_shiftX<=-750) then 
        begin
            g_shiftX:=750;
            showLogo:=false;
        end;
    end;

    if not showLogo then
    begin
        BitBlt(g_MemDC,0,0,WINDOWN_WIDTH,750-g_shiftX,g_bufDC,0,750+g_shiftX,SRCCOPY);
        BitBlt(g_MemDC,0,0,WINDOWN_WIDTH,WINDOWN_HEIGHT-g_shiftX-750,g_bufDC,0,750+g_shiftX,SRCCOPY);
        if(g_shiftX<=0) then 
        begin
            g_shiftX:=750;
        end;
    end;
    dec(g_shiftX,5);
    //处理背景 结束

    //碰撞检测
    p:=eneny;
    while(p^.next<>nil) do
      begin
        //和自己的战机检测
        if (checkMeet(p^.next^.x, p^.next^.y, enenyWidth, enenyHeight, plane^.x, plane^.y, planeWidth, planeHeight)) then
        begin
          killtimer(window,1);
          MessageBoxW(window,'游戏结束！','Game over!',MB_OK);
          //ExitProcess(0);
          break;
        end;
        
        //
        temp:=bullet;
        while(temp^.next<>nil) do
          begin
            if (checkMeet(p^.next^.x, p^.next^.y, enenyWidth, enenyHeight, temp^.next^.x, temp^.next^.y, bulletWidth, bulletHeight)) then
                begin
                    deltemp:=temp^.next;
                    temp^.next:=temp^.next^.next;
                    dispose(deltemp);
                    deltemp:=p^.next;
                    p^.next:=p^.next^.next;
                    dispose(deltemp);
                    break;
                end;
            temp:=temp^.next;
          end;
        p:=p^.next;
        if(p=nil)then
          break;
      end;

    // 自已的
    SelectObject(g_bufDC,planeBMP);
    BitBlt(g_MemDC, plane^.y, plane^.x, planeWidth, planeHeight, g_bufDC, 0, 0, SRCAnd);
    BitBlt(g_MemDC, plane^.y, plane^.x, planeWidth, planeHeight, g_bufDC,planeWidth, 0, SRCPaint);

    //敌人
    p:=eneny^.next;
    while(p<>nil)do
    begin
        SelectObject(g_bufDC,enenyBMP);
        BitBlt(g_MemDC, p^.y, p^.x, enenyWidth, enenyHeight, g_bufDC, 0, 0, SRCAnd);
        BitBlt(g_MemDC, p^.y, p^.x, enenyWidth, enenyHeight, g_bufDC, enenyWidth, 0, SRCPaint);
        inc(p^.x,8);
        p:=p^.next;
    end;

    //敌人出界的
    p:=eneny;
    while(p^.next<>nil)do
    begin
        if(p^.next^.x>(MaxBottom+enenyHeight)) then
        begin
          delAfterNode(p);
          p^.next:=nil;
          break;
        end;
        p:=p^.next;
    end;


    //子弹
     p:=bullet^.next;
    while(p<>nil)do
    begin
        SelectObject(g_bufDC,bulletBMP);
        BitBlt(g_MemDC, p^.y, p^.x, bulletWidth, bulletHeight, g_bufDC, 0, 0, SRCAnd);
        BitBlt(g_MemDC, p^.y, p^.x, bulletWidth, bulletHeight, g_bufDC,bulletWidth, 0, SRCPaint);
        dec(p^.x,10);
        p:=p^.next;
    end;

    //子弹出界
    p:=bullet;
    while(p^.next<>nil)do
    begin
        if(p^.next^.x<(-bulletHeight)) then
        begin
          delAfterNode(p);
          p^.next:=nil;
          break;
        end;
        p:=p^.next;
    end;
end;

{释放数据}
procedure gamefree(window:HWND);
begin
   
    killtimer(window,1);
 
    if plane<> nil then dispose(plane);

    delAfterNode(eneny);
    dispose(eneny);

    delAfterNode(bullet);
    dispose(bullet);

    DeleteObject(bgBMP);
    DeleteObject(planeBMP);
    DeleteObject(bulletBMP);
    DeleteObject(enenyBMP);
    DeleteDC(g_bufDC);
    DeleteDC(g_MemDC);
    ReleaseDC(window, g_hdc);
    DeleteDC(g_hdc);
end;

{增加游戏元素 1子弹，2敌人}
procedure addNode(style:integer);
var
    p:pnode;
begin
    //增加子弹
    if style=BulletStyle then
        begin
            new(p);
            p^.next:=bullet^.next;
            p^.x := Plane^.x;
            p^.y := Plane^.y +  planeWidth div 2 - bulletWidth div 2;
            bullet^.next:=p;
        end
    else if(style=EnenyStyle) then
        begin
            new(p);
            p^.next:=eneny^.next;
            p^.x :=0;
            p^.y := Random(MaxRight);
            eneny^.next:=p;
        end
    else
        p:=nil;
end;

{删除后面元素}
procedure delAfterNode(l:pnode);
var
    p:pnode;
begin
    if l^.next=nil then exit;
    p:=l;
    while(p^.next^.next<>nil) do
        delAfterNode(p^.next);
    l^.next:=nil;
    dispose(p^.next);
end;

{两个距形碰撞}
function checkMeet(x1,y1,w1,h1,x2,y2,w2,h2:longint):boolean;
begin
    if (x1 >= x2) and (x1 >= (x2 + w2)) then begin  
            exit( false);
        end else if (x1 <= x2) and ((x1 + w1) <= x2) then begin  
            exit (false); 
        end else if (y1 >= y2) and (y1 >= (y2 + h2)) then begin  
            exit (false);  
        end else if (y1 <= y2) and ((y1 + h1) <= y2) then begin  
            exit (false);  
        end; 
        exit (true); 
end;

{消息处理}
function WindowProc(Window: HWnd; AMessage: UINT; WParam : WPARAM;
            LParam: LPARAM): LRESULT; stdcall; export;
var
    r : RECT;
begin
    WindowProc := 0;
    case AMessage of
            wm_create:
                begin
                    //游戏初始化
                    gameinit(window);
                end;
            wm_paint:
                begin
                    //gamepaint(window);
                    BitBlt(g_hdc,0,0,WINDOWN_WIDTH,WINDOWN_HEIGHT,g_MemDC,0,0,SRCCOPY);
                end;
            wM_ERASEBKGND:
                exit(1);
            wm_timer:{时钟}
                begin
                    gamepaint(window);                   
                    GetClientRect(window,@r);
                    InvalidateRECT(window,r,true);
                    UpdateWindow(Window); 
                end; 
            wm_keyDown:{k}   
                begin
                    case wParam of
                    vk_up:  if (plane^.x -skip) >=0 then    plane^.x -= skip;
                    vk_down: if (plane^.x +skip+planeHeight) <=MaxBottom then    plane^.x += skip;
                    vk_left: if (plane^.y -skip) >=0 then    plane^.y -= skip;
                    vk_right: if (plane^.y + skip + planeWidth) <=MaxRight then    plane^.y += skip;
                    vk_space: addNode(BulletStyle);
                    end;
                end;
            wm_Destroy: { 关闭 }
            begin
                gamefree(window);
                PostQuitMessage(0);
                exit;
            end;
    end;
    WindowProc := DefWindowProc(Window, AMessage, WParam, LParam);
end;

{ Register the Window Class }
function WinRegister: Boolean;
var
    WindowClass: WndClass;
begin
    WindowClass.Style := cs_hRedraw or cs_vRedraw;
    WindowClass.lpfnWndProc := WndProc(@WindowProc);
    WindowClass.cbClsExtra := 0;
    WindowClass.cbWndExtra := 0;
    WindowClass.hInstance := system.MainInstance;
    WindowClass.hIcon := LoadIcon(0, idi_Application);
    WindowClass.hCursor := LoadCursor(0, idc_Arrow);
    WindowClass.hbrBackground := GetStockObject(WHITE_BRUSH);
    WindowClass.lpszMenuName := nil;
    WindowClass.lpszClassName := AppName;
    Result := RegisterClass(WindowClass) <> 0;
end;{End Register}

{ Create the Window Class }
function WinCreate: HWnd;
var
    hWindow: HWnd;
begin
    hWindow := CreateWindowW(AppName, AppTitle,
            ws_OverlappedWindow, cw_UseDefault, cw_UseDefault,
            WINDOWN_WIDTH, WINDOWN_HEIGHT, 0, 0, system.MainInstance, nil);

    if hWindow <> 0 then begin
        ShowWindow(hWindow, CmdShow);
        ShowWindow(hWindow, SW_SHOW);
        UpdateWindow(hWindow);
    end;

    Result := hWindow;
end;{end create }

{main}
var
    AMessage : Msg;
    hWindow : hwnd;
    nowTime : DWORD;
begin
    Randomize;
    if not WinRegister then begin
        MessageBox(0, 'Register failed', nil, mb_Ok);
        Exit;
    end;
    hWindow := WinCreate;
    if longint(hWindow) = 0 then begin
        MessageBox(0, 'WinCreate failed', nil, mb_Ok);
        Exit;
    end;

    SetTimer(hWindow,1,50,nil);

    while GetMessage(@AMessage, 0, 0, 0) do begin
        nowTime:=getTickCount();
        if gaming and ((nowTime-g_preTime) >= (500+ Random(500))) then
        begin
            addNode(EnenyStyle);
            g_preTime:=nowTime;
        end;
        
        TranslateMessage(AMessage);
        DispatchMessage(AMessage);
        
      end;
     Halt(AMessage.wParam);
end. {end for main}